"""ofx2n3.py -- interpret OFX format as RDF

Converts OFX format (as in downloaded back statements etc

  The conversion is only syntactic.  The OFX modelling is
  pretty weel thought out, so taking it as defining an effecive
  RDF ontolofy seems to make sense. Rules can then be used to
  define mapping into your favorite ontology.

DESIGN NOTES

  The properties have even been left in upper
  case, although I wouldn't do that again next time.
  The SGML/XML tree is converted into a tree of blank nodes.
  This is made easier by the rule that OFX does not allow empty elements
  or mixed content.

  OFX actually defines a request-response protocol using HTTP and
  SGML (v1.*) or  XML (v2.*).
  I have only had access to downloaded statements which look like HTTP
  responses carrying SGML, so that is what this handles.

REFERENCES

This converts data from the  common proprietary format whcih seems
to be in use.  The spec i found is a later XML-based version, which will
be much simpler. Alas the spec not served directly on the web.
"Open" Financial Exchange
   Specification 2.0 
   April 28, 2000 (c) 2000 Intuit Inc., Microsoft Corp.

We try to stick to:
Python Style Guide
  Author: Guido van Rossum
  http://www.python.org/doc/essays/styleguide.html


LICENSE OF THIS CODE

Workspace: http://www.w3.org/2000/10/swap/pim/financial/

Copyright 2002-2003 World Wide Web Consortium, (Massachusetts
Institute of Technology, European Research Consortium for
Informatics and Mathematics, Keio University). All Rights
Reserved. This work is distributed under the W3C(R) Software License
  http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231
in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE.

This was http://www.w3.org/2000/10.swap/pim/financial/OFX-to-n3.py
"""
help="""Convert OFX file to OFX-RDF (in memory) and load to Sesame server.
"""

__version__ = "$Id: ofx2n3.py 161 2009-10-25 00:18:28Z udi.benreuven $"
thisSource = "http://www.w3.org/2000/10.swap/pim/financial/OFX-to-n3.py"
import os
import sys
import re
import logging
from n3tagvalue import stagvalue,n3header

reHeader = re.compile(r"""\s*
(OFXHEADER:(?P<OFXHEADER>\d+)\s*)
(DATA:(?P<DATA>[A-Z][A-Z0-9]*?)\s*)
(VERSION:(?P<VERSION>\d*)\s*) # do a non-greedy match, not eat the next tag
(SECURITY:(?P<SECURITY>[A-Z][A-Z0-9]*?)\s*)
(ENCODING:(?P<ENCODING>[A-Z][A-Z0-9]*?)\s*)
(CHARSET:(?P<CHARSET>\d+)\s*)
(COMPRESSION:(?P<COMPRESSION>[A-Z][A-Z0-9]*?)\s*)
(OLDFILEUID:(?P<OLDFILEUID>[A-Z][A-Z0-9]*?)\s*)
(NEWFILEUID:(?P<NEWFILEUID>[A-Z][A-Z0-9]*)\s*)
""",re.X) #
reTag = re.compile(r"\s*<(?P<endtag>/)?(?P<tag>[A-Z][A-Z0-9\.]+)>")
reValue = re.compile(r"([^<]*)")

def flat(fin,fout,context=None,hashing=False):
    if isinstance(fin,str):
        fin=open(fin)
    else:
        fin.seek(0)
    doc = fin.read() # Process the content as a singel buffer
    
    n3header(fout,"$Id$"[1:-1],context)
    print >>fout,"  ofxh:headers ["
    doc=doc.strip()
    stack = []
    header = reHeader.match(doc)
    if not header: raise SyntaxError("Can't find header")
    pos = header.end()
    header = header.groupdict()
    for tag,value in header.iteritems():
        print >>fout,"  ofxh:%s \"%s\";" % (tag, value)  #@@ do n3 escaping
    print >>fout,"];\n"

    # Check our assumptions on header
    if header["ENCODING"] != "USASCII":
        raise SyntaxError('ENCODING:%s should be USASCII'%header["ENCODING"])
    if header["OFXHEADER"] != "100":
        raise SyntaxError('OFXHEADER:%s should be 100'%header["OFXHEADER"])
    if header["VERSION"] != "102":
        raise SyntaxError('VERSION:%s should be 102'%header["VERSION"])

    valuetag=None
    while pos < len(doc):
        tag = reTag.match(doc,pos)
        if not tag:
            raise SyntaxError("No Tag %s..."%doc[pos:pos+20])
        pos = tag.end()
        endtag = tag.group("endtag")
        tag = tag.group("tag")
        if endtag:
            if tag != valuetag:
                tag2 = stack.pop()
                if tag != tag2: raise SyntaxError(
                    "Found </%s> when </%s> expected.\nStack: %s" % 
                    (tag, tag2, stack))
                print >>fout,"%s];  # %s" % ("  "*len(stack), tag)
            valuetag=None
        else:
            value = reValue.match(doc,pos)
            if value:
                pos = value.end()
                value = value.group(1).strip()
            else:
                value=""
            if not value: # Start tag
                valuetag=None
                print >>fout,"%s ofx:%s [" %("  "*len(stack), tag)
                stack.append(tag)
            else:   #  Data tag
                valuetag=tag
                print  >>fout," "*len(stack),stagvalue(tag,value,hashing=hashing)

    if stack: raise SyntaxError("Unclosed tags: %s" % stack)
    print >>fout,"."
